﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using ArtMath.Core.Data;
using ArtMath.PointCollection;
using ArtMath.Attribute;
using ArtMath.Converter;
using ArtMath.Component.CoordinateSystem;
using ArtMath.Core.Helper;
using ArtMath.CommonType;

namespace ArtMath.Component.Circle
{
    /// <summary>
    /// （封闭）类圆基类
    /// </summary>
    [Serializable]
    public abstract class CircleLikeBase : ClosedShapeBase, ICircle
    {
        protected PointCollection.Circle ske;// = new Circle() { Curves = new Curve[] { new Curve() } };
        protected Ellipse pske;// = new Ellipse() { Curves = new Curve[] { new Curve() } };
        protected List<KeyValuePair<PointD, double[]>> track = new List<KeyValuePair<PointD, double[]>>();
        protected Curve newCur = new Curve(), newPCur = new Curve();
        protected PropertyDisplayer rDisplayer, ctDisplayer;
        public CircleLikeBase(SketchPad pad, string name, ToolTypeName toolType, bool addToList = true) : base(pad, name, toolType, addToList)
        {
            RadiusDisplayer = new PropertyDisplayer(this, "Radius");
            CenterDisplayer = new PropertyDisplayer(this, "Center");
        }
        public override PointCollectionsManager Skeleton
        {
            get
            {
                return new PointCollectionsManager(ske);
            }
        }
        public override PointCollectionsManager PSkeleton
        {
            get
            {
                return new PointCollectionsManager(pske);
            }
        }
        public override PointD MassPoint
        {
            get { return Center; }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("CircleCenter"), TypeConverter(typeof(IntelliPointConverter))]
        public virtual PointD Center
        {
            get { return ske.Center; }
        }
        [Browsable(false)]
        public virtual PointD NewPCenter
        {
            get { return PCenter; }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("Radius"), ReadOnly(true)]
        public double Radius
        {
            get { return ske.Radius; }
            protected set
            {
                ske.Radius = value;
                if (Pad.Coordinate == null)
                {
                    pske.LongAxisHalfLength = pske.ShortAxisHalfLength = ske.Radius;
                }
                else
                {
                    pske.LongAxisHalfLength = Pad.Coordinate.GetPx(ske.Radius);
                    pske.ShortAxisHalfLength = Pad.Coordinate.GetPy(ske.Radius);
                }
                Pad.Canvas.Invalidate();
            }
        }
        //[Browsable(false)]
        //public double XAxisHalfLength
        //{
        //    get { return Px; }
        //}
        //[Browsable(false)]
        //public double YAxisHalfLength
        //{
        //    get { return Py; }
        //}
        [Browsable(false)]
        public virtual double NewRadius
        {
            get { return ske.Radius; }
        }
        [Browsable(false)]
        public PointD PCenter
        {
            get { return pske.Center; }
        }

        [Browsable(false)]
        public virtual PointD NewCenter
        {
            get { return Center; }
        }
        public override bool PointsHaveConstraint
        {
            get { return false; }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "Radius", "Displayer")]
        public PropertyDisplayer RadiusDisplayer
        {
            get { return rDisplayer; }
            set { rDisplayer = value; }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "CircleCenter", "Displayer")]
        public virtual PropertyDisplayer CenterDisplayer
        {
            get { return ctDisplayer; }
            set { ctDisplayer = value; }
        }
        //[Browsable(false)]
        //public IList<Curve> Curves
        //{
        //    get
        //    {
        //        return ske.Curves;
        //    }
        //}
        //[Browsable(false)]
        //public virtual IList<Curve> NewCurves
        //{
        //    get
        //    {
        //        return new Curve[] { newCur };
        //    }
        //}
        //[Browsable(false)]
        //public IList<Curve> PCurves
        //{
        //    get
        //    {
        //        return pske.Curves;
        //    }
        //}
        //[Browsable(false)]
        //public virtual IList<Curve> NewPCurves
        //{
        //    get
        //    {
        //        return new Curve[] { newPCur };
        //    }
        //}
        public override PointD AnchorPoint(PropertyDisplayer disp)
        {
            return PCenter;
        } 
        public override void ClearTracks()
        {
            track.Clear();
            base.ClearTracks();
        }
        public override void CheckInRegion(RectangleD rect)
        {
            SetInPicker(rect.IntersectCurves(pske.Curves));
            base.CheckInRegion(rect);
        }
        //public override void PointMoving_AsOutHostLimit(RPoint movePoint, PointD newCoordinate)
        //{
        //    movePoint.LimitedPxy = newCoordinate.GetNearestPointFromCurves(new Curve[] { cur });
        //}
        //public override bool OnObject(PointD pt)
        //{
        //    return Radius.AlmostEqual(Center.DistanceTo(pt));
        //}
        public override void ComponentBuilding(Base component)
        {
            setFillRegion();
            base.ComponentBuilding(component);
        }
        public override void ComponentChanged(ShapeBase component)
        {
            ske.Center = NewCenter;
            pske.Center = NewPCenter;
            Radius = NewRadius;
            setFillRegion();
            base.ComponentChanged(component);
        }
        void setFillRegion()
        {
            if (ToolType == ToolTypeName.sdgx || ToolType == ToolTypeName.xggx)
            {
                FillRegion = Utility.GetSmallestSurroundRect(pske.Curves);
            }
            else
            {
                FillRegion = new RectangleD(-pske.LongAxisHalfLength, -pske.ShortAxisHalfLength, pske.LongAxisHalfLength * 2, pske.ShortAxisHalfLength * 2);
            }
        }
        protected override string getFormula()
        {
            if (Pad.CoordinateType == CoordinateSystemType.PolarCoordinate)
            {
                PointD rt = (Pad.Coordinate as PolarCoordinate).XYtoRT(Center, false);
                return $"ρ^2+{rt.X}^2-{2 * rt.X}ρcos(θ-{rt.Y})={Radius * Radius}";
            }
            else
            {
                return $"(x-{Center.X})^2+(y-{Center.Y})^2={Radius * Radius}";
            }
        }
        public override void RefreshPropertyDisplayersLocation()
        {
            CenterDisplayer.RefreshLocation();
            RadiusDisplayer.RefreshLocation();
            base.RefreshPropertyDisplayersLocation();
        }
        public override void RefreshPropertyDisplayersText()
        {
            CenterDisplayer.RefreshText();
            RadiusDisplayer.RefreshText();
            base.RefreshPropertyDisplayersText();
        }
        public override PropertyDisplayer[] PropertyDisplayersCheckNearBy(PointD Plocation)
        {
            List<PropertyDisplayer> res = new List<PropertyDisplayer>(base.PropertyDisplayersCheckNearBy(Plocation));
            if (RadiusDisplayer.InRegion(Plocation))
            {
                res.Add(RadiusDisplayer);
            }
            if (CenterDisplayer.InRegion(Plocation))
            {
                res.Add(CenterDisplayer);
            }
            return res.ToArray();
        }
        public override void PaintProperties(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible)
            {
                CenterDisplayer.Paint(gra);
                RadiusDisplayer.Paint(gra);
            }
            base.PaintProperties(gra);
        }
        public double? GetTangentLineSlope(PointD pt)
        {
            //if (!OnLine(pt))
            //{
            //    if (Pad.Coordinate != null) pt = Pad.Coordinate.XYToPxy(pt);
            //    pt = pt.GetNearestPointFromCurves(NewCurves);
            //    if (Pad.Coordinate != null) pt = Pad.Coordinate.PxyToXY(pt);
            //}
            return -1 / NewCenter.GetSlope(pt);
        }
        public abstract PointD[] GetTangentPoints(PointD passPoint);
    }
}
